package org.hzero.business;

import io.choerodon.core.exception.CommonException;
import org.activiti.rest.service.api.engine.variable.RestVariable;
import org.activiti.rest.service.api.runtime.process.ProcessInstanceCreateRequest;
import org.activiti.rest.service.api.runtime.process.ProcessInstanceResponse;
import org.checkerframework.checker.units.qual.K;
import org.hzero.business.workflow.builder.KnifeWorkflowRequestBuilder;
import org.hzero.business.workflow.builder.director.KnifeWorkflowRequestDirector;
import org.hzero.business.workflow.builder.impl.SimpleRequestBuilder;
import org.hzero.business.workflow.dto.KnifeWorkflowVar;
import org.hzero.business.workflow.mapper.KnifeWorkflowMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @ClassName: WorkFlowUtil
 * @Description: 工作流工具类
 * @author: tianhao.luo@hand-china.com
 * @date: 2020/10/26  11:22
 */
@Component
public final class KnifeWorkflowUtil {

    private static final Integer IS_REQUIRED=1;
    private static final Integer IS_NOT_REQUIRED=0;

    @Autowired
    private KnifeWorkflowMapper knifeWorkflowMapper;


    /**
     * 查出是否有审批记录了
     * @param businessKey 业务主键
     * @return 审批记录id
     */
    public List<Long> listApprovedByBusinessKey(String businessKey){
        Assert.isTrue(!StringUtils.isEmpty(businessKey),"业务主键不能为空！");
        return knifeWorkflowMapper.selectActHiCommentId(businessKey);
    }

    /**
     * 校验工作流是否启动成功
     * @param entity 返回结果
     */
    public static void checkStartUp(ResponseEntity<ProcessInstanceResponse> entity){
        if (ObjectUtils.isEmpty(entity)){
            throw new CommonException("工作流启动失败");
        }else {
            ProcessInstanceResponse body = entity.getBody();
            HttpStatus statusCode = entity.getStatusCode();
            if (ObjectUtils.isEmpty(body) || StringUtils.isEmpty(body.getId()) || statusCode.isError()){
                throw new CommonException("工作流启动失败");
            }
        }
    }

    /**
     * 校验启动工作流的流程变量是否输入完整
     * @param request 启动流程的请求参数
     * @param requiredVarList 必输流程变量
     */
    private void checkStartVar(ProcessInstanceCreateRequest request,List<String> requiredVarList){
        List<String> startUpVarList = request.getVariables().stream().map(RestVariable::getName).collect(Collectors.toList());
        boolean flag = false;
        StringBuilder sb = new StringBuilder();
        for (String requireVar :
                requiredVarList) {
            //启动的流程变量list中，没有匹配到requireVar的
            if (startUpVarList.stream().noneMatch(item -> item.equals(requireVar))) {
                flag = true;
                if (sb.length() == 0){
                    sb.append(requireVar);
                }else {
                    sb.append(",").append(requireVar);
                }
            }
        }
        if (flag){
            throw new CommonException("请联系管理员给流程变量"+sb.toString()+"赋值");
        }
    }

    /**
     * 校验启动工作流的流程变量是否输入完整
     * @param request 启动流程的请求参数
     * @param processKey 必输流程变量
     */
    public void checkStartVar(ProcessInstanceCreateRequest request,String processKey){
        Assert.isTrue(!StringUtils.isEmpty(processKey),"流程变量key不能为空！");
        // 启动时创建的流程变量
        List<String> startUpVarList = request.getVariables().stream().map(RestVariable::getName).collect(Collectors.toList());
        // 流程定义时的流程变量
        List<KnifeWorkflowVar> categoryVariableList = knifeWorkflowMapper.listCategoryVariableById(processKey,IS_REQUIRED);
        List<String> categoryVarNameList = categoryVariableList.stream().map(KnifeWorkflowVar::getVariableName).collect(Collectors.toList());
        List<KnifeWorkflowVar> documentVariableList = knifeWorkflowMapper.listDocumentVariableById(processKey,IS_REQUIRED);
        List<String> documentVarNameList = documentVariableList.stream().map(KnifeWorkflowVar::getVariableName).collect(Collectors.toList());

        List<String> formUrls = knifeWorkflowMapper.listFormUrlByProcessKey(processKey);

        List<String> requiredVarList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(categoryVarNameList)){
            requiredVarList.addAll(categoryVarNameList);
        }
        if (!CollectionUtils.isEmpty(documentVarNameList)){
            requiredVarList.addAll(documentVarNameList);
        }

        //校验流程单据及分类上定义的流程变量是否已经包含了流程表单中的url
        // 流程表单中url为空则无需校验了
        if (!CollectionUtils.isEmpty(formUrls)){
            HashSet<String> formUrlVarSet = new HashSet<>();
            for (String url :
                    formUrls) {
                List<String> varFormUrl = getVarFormUrl(url);
                formUrlVarSet.addAll(varFormUrl);
            }
            //校验varList即配置的流程变量中是否已经包含了表单中的流程变量
            StringBuilder varSb = new StringBuilder();
            formUrlVarSet.forEach(item ->{
                if (!requiredVarList.contains(item)) {
                    if (varSb.length() == 0){
                        varSb.append(item);
                    }else {
                        varSb.append(",").append(item);
                    }
                }
            });
            Assert.isTrue(varSb.length() ==0,"请在流程定义:"+processKey+"使用的流程单据上配置表单所需流程变量："+varSb.toString());
        }


        boolean flag = false;
        StringBuilder sb = new StringBuilder();
        for (String requireVar :
                requiredVarList) {
            //启动的流程变量list中，没有匹配到requireVar的
            if (startUpVarList.stream().noneMatch(item -> item.equals(requireVar))) {
                flag = true;
                if (sb.length() == 0){
                    sb.append(requireVar);
                }else {
                    sb.append(",").append(requireVar);
                }
            }
        }
        if (flag){
            throw new CommonException("请联系管理员给流程变量"+sb.toString()+"赋值");
        }
    }

    /**
     * 从流程表单中解析出流程变量
     * @param s 流程表单url，例如relative://pub/aatn/asset-transaction-basic-type/accident/detail/${transactionTypeId}/${headerId}/false
     * @return 流程变量名
     */
    public static List<String> getVarFormUrl(String s){
        Assert.isTrue(!StringUtils.isEmpty(s),"待解析的流程表单url不能为空！");
        List<String> strings = new ArrayList<>();
        // 流程表单中的变量都是用${}来包裹的，所以解析的方式就是，从左往右开始解析
        int i = s.indexOf("{");
        int end = s.indexOf("}");
        if (i != -1 && end !=-1){
            // url截取变量
            String substring = s.substring(i+1, end);
            // 保存第一个流程变量
            strings.add(substring);
            // 舍弃掉此流程变量前（包括）的字符串
            String newFromUrl = s.substring(0, end+1);
            String replace = s.replace(newFromUrl, "");
            // 此处的strings.addAll是能够返回正确数据的关键，因为这个递归，走到最里面之后出栈时
            // 会把本次的list数据全部添加到上一次的list变量中，直到完全出栈
            strings.addAll(getVarFormUrl(replace));
        }
        return strings;
    }

    /**
     * 构造工作流启动必传请求参数
     * @param paramMap 流程变量参数map
     * @param processDefineKey 流程定义key
     * @param businessKey 业务主键key
     * @return 工作流启动必传参数
     */
    public ProcessInstanceCreateRequest getRequest(Map<String, Object> paramMap, String processDefineKey, String businessKey){
        Assert.isTrue(!StringUtils.isEmpty(processDefineKey),"流程变量key不能为空！");
        // 获取流程定义的所有流程变量
        List<KnifeWorkflowVar> categoryVariableList = knifeWorkflowMapper.listCategoryVariableById(processDefineKey, null);
        List<KnifeWorkflowVar> documentVariableList = knifeWorkflowMapper.listDocumentVariableById(processDefineKey, null);
        // 获取自定义工作流请求构造者
        KnifeWorkflowRequestBuilder simpleRequestBuilder = new SimpleRequestBuilder();
        KnifeWorkflowRequestDirector knifeWorkflowRequestDirector = new KnifeWorkflowRequestDirector(simpleRequestBuilder);
        //通过List构造流程变量
        List<KnifeWorkflowVar> allVariableList = new ArrayList<KnifeWorkflowVar>(categoryVariableList);
        allVariableList.addAll(documentVariableList);
        // 如果变量list为空则不走下面
        for (KnifeWorkflowVar knifeWorkflowVar : allVariableList) {
            if (null != paramMap.get(knifeWorkflowVar.getVariableName())){
                //构造流程变量
                knifeWorkflowRequestDirector.createRestVariable(knifeWorkflowVar.getVariableName(),
                        paramMap.get(knifeWorkflowVar.getVariableName()),
                        knifeWorkflowVar.getVariableType(),
                        null);
            }
        }
        return knifeWorkflowRequestDirector.buildRequest(processDefineKey, businessKey);
    }
}
